﻿
/*#define BOOST_ASIO_HAS_CO_AWAIT*/
#include <doctest/doctest.h>
#include <filesystem>
#include <thread>

#include <coroutine>
//#include "asio.hpp"
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/read.hpp>
#include <boost/asio/signal_set.hpp>
#include <boost/asio/this_coro.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <boost/asio/write.hpp>

#include "log.h"

using boost::asio::awaitable;
using boost::asio::co_spawn;
using boost::asio::detached;
using boost::asio::use_awaitable;
using boost::asio::ip::tcp;
namespace this_coro = boost::asio::this_coro;


#if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
# define use_awaitable \
  boost::asio::use_awaitable_t(__FILE__, __LINE__, __PRETTY_FUNCTION__)
#endif

using namespace boost;
using namespace boost::asio;
//
TEST_CASE(" asio_server_synchronization") {
    using namespace std;
    using namespace /*boost::*/asio;
    try
    {
        typedef ip::tcp::acceptor acceptor_type;
        typedef ip::tcp::endpoint endpoint_type;
        typedef ip::tcp::socket socket_type;

        io_service io;//io_server对象
        acceptor_type acceptor(io, endpoint_type(ip::tcp::v4(), 6688)); //acceptor对象（注有多种构造函数形式，这是其中一种形式）
        cout << acceptor.local_endpoint().address() << endl;

        for (;;)
        {
            socket_type sock(io);//定义socket对象

            acceptor.accept(sock);//阻塞直到收到新的socket连接，并将socket连接对象由引用参数输出

            cout << "client:";
            cout << sock.remote_endpoint().address() << ":" << sock.remote_endpoint().port() << endl;

            sock.send(buffer("hello asio"));
        }
    }
    catch (std::exception &e)
    {
        cout << e.what() << endl;
    }

}

//
TEST_CASE(" client_asio_synchronization") {
    using namespace std;
    using namespace /*boost::*/asio;

    try
    {
        typedef ip::tcp::endpoint endpoint_type;
        typedef ip::tcp::socket socket_type;
        typedef ip::address address_type;

        io_service io; //io_server对象
        socket_type sock(io); //socket对象
        endpoint_type ep(address_type::from_string("127.0.0.1"), 6688);//端点

        sock.connect(ep);//socket连接端点，同步的，要么连接成功返回，要么抛出异常
        cout << sock.available() << endl;

        //vector<char> str(sock.available() + 1, 0);
        //sock.receive(buffer(str));
        //cout << "recive from " << sock.remote_endpoint().address();
        //cout << &str[0] << endl;

        vector<char> str(5, 0);
        boost::system::error_code ec;
        //asio::error_code ec;
        for (;;)
        {
            sock.read_some(buffer(str), ec); //socket读取内容
            if (ec)
            {
                break;
            }
            cout << &str[0];
        }
        cout << endl;
    }
    catch (std::exception &e)
    {
        cout << e.what() << endl;
    }
}

using namespace std;

using namespace boost;

class HttpConnection : public std::enable_shared_from_this<HttpConnection> {
public:
    HttpConnection(asio::io_context &io)
            : socket_(io) {
    }

    void Start() {
        auto p = shared_from_this();
        asio::async_read_until(socket_, asio::dynamic_buffer(request_), "\r\n\r\n",
                               [p, this](const error_code &err, size_t len)
                               {
                                   if (err)
                                   {
                                       cout << "recv err:" << err.message() << "\n";
                                       return;
                                   }
                                   string first_line = request_.substr(0, request_.find(
                                           "\r\n")); // should be like: GET / HTTP/1.0
                                   cout << first_line << "\n";
                                   // process with request
                                   // ...

                                   char str[] = "HTTP/1.0 200 OK\r\n\r\n"
                                                "<html>hello from http server</html>";
                                   asio::async_write(socket_, asio::buffer(str),
                                                     [p, this](const error_code &err, size_t len)
                                                     {
                                                         socket_.close();
                                                     });
                               });
    }

    asio::ip::tcp::socket &Socket() {
        return socket_;
    }

private:
    asio::ip::tcp::socket socket_;
    string request_;
};

class HttpServer {
public:
    HttpServer(asio::io_context &io, const asio::ip::tcp::endpoint &ep)
            : io_(io), acceptor_(io, ep) {
    }

    void Start() {
        auto p = std::make_shared<HttpConnection>(io_);
        acceptor_.async_accept(p->Socket(), [p, this](const error_code &err)
        {
            if (err)
            {
                cout << "accept err:" << err.message() << "\n";
                return;
            }
            p->Start();
            Start();
        });
    }

private:
    asio::io_context &io_;
    asio::ip::tcp::acceptor acceptor_;
};

TEST_CASE(" chat") {

    try
    {
        asio::io_context io;
        asio::ip::tcp::endpoint ep(asio::ip::make_address("127.0.0.1"), std::stoi("8080"));
        HttpServer hs(io, ep);
        hs.Start();
        io.run();
    }
    catch (const std::exception &e)
    {
        std::cout << e.what() << std::endl;
        LOG_INFO << e.what();
    }

}


void signal_handler(int signal)
{
    std::cout << "Received signal " << signal << '\n';
}
#ifdef WIN32
void log(std::string_view message,
         const std::source_location& location = std::source_location::current())
{
    std::cout << "info:"
              << location.file_name() << ':'
              << location.line() << ' '
              << message << '\n';
}
#else
void log(std::string_view message){

}
#endif

TEST_CASE("test_one") {

    try
    {
        asio::io_context io_context;

        asio::ip::tcp::socket socket(io_context);

        asio::ip::tcp::endpoint server_endpoint(asio::ip::make_address("127.0.0.1"), std::stoi("8080"));

        boost::system::error_code ec;

        socket.connect(server_endpoint, ec);

        signal(SIGTERM, signal_handler);

        log("Hello world!");
#if WIN32
        if (ec)
        {
            std::printf("error %s", ec.what().c_str());
        }
#endif
    }
    catch (const std::exception &e)
    {
        std::cout << e.what() << std::endl;
        LOG_INFO << e.what();
    }

}

using namespace asio::ip;


awaitable<void> echoTest(tcp::socket socket)
{
    try
    {
        char data[1024];
        for (;;)
        {
            std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable);
            co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable);
        }
    }
    catch (std::exception& e)
    {
        std::printf("echo Exception: %s\n", e.what());
    }
}

awaitable<void> listenerTest()
{
    auto executor = co_await this_coro::executor;
    tcp::acceptor acceptor(executor, {tcp::v4(), 55555});
    for (;;)
    {
        tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
        co_spawn(executor, echoTest(std::move(socket)), detached);
    }
}

TEST_CASE("coroutines_asio_cat") {
{
    try
    {
        boost::asio::io_context io_context(1);
        
        boost::asio::signal_set signals(io_context, SIGINT, SIGTERM);
        signals.async_wait([&](auto, auto){ io_context.stop(); });

        // 执行协程，并设置了一个callback函数来获取这个协程的返回值
        co_spawn(io_context, listenerTest(), detached);

        io_context.run();
    }
    catch (std::exception& e)
    {
        std::printf("Exception: %s\n", e.what());
    }
}

}

struct Result{
    struct promise_type {
        Result get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void unhandled_exception() {}

        suspend_always yield_value(){} // 对应co_yield
        void return_void() {}	//对应co_return
        Result return_value(const Result& res){ return res;}
    };
};

/*TEST_CASE(" coroutines_asio") {
    asio::io_context ctx;
    asio::ip::tcp::endpoint endpoint{asio::ip::make_address("220.181.38.148"), 80};
    asio::ip::tcp::socket socket{ctx};

    boost::asio::co_spawn(ctx, http_request(std::move(socket), endpoint), asio::detached);

    ctx.run();

}*/
/*
awaitable<void> echo(asio::ip::tcp::socket socket) {
    try {
        char data[1024];
        size_t n = 0;
        for (;;) {
            n = co_await socket.async_read_some(asio::buffer(data), use_awaitable);
            co_await async_write(socket, asio::buffer(data, n), use_awaitable);
        }
    } catch (std::exception& e) { ... }
}

awaitable<void> listener() {
    auto executor = co_await this_coro::executor;
    tcp::acceptor acceptor(executor, {tcp::v4(), 55555});
    for (;;) {
        tcp::socket socket = co_await acceptor.async_accept(use_awaitable);
        co_spawn(executor, echo(std::move(socket)), detached);
    }
}

int main() {
    asio::io_context io_context(1);
    asio::signal_set signals(io_context, SIGINT, SIGTERM);
    signals.async_wait([&](auto, auto) { io_context.stop(); });
    co_spawn(io_context, listener(), detached);
    io_context.run();
    return 0;
}
*/

